using UnityEngine;
using System.Collections;

public class EnemyController : MonoBehaviour {
	public Transform Fireball;
	public Transform Explosion;
	public Transform Shard;
	
	public Texture DamagedTexture;
	public Texture DamagedTexture2;

	// property
	public float MaxHealth = 100.0f;
	public float MoveSpeed = 80.0f;
	public float RotSpeed = 25.0f;
	public int	 PercentToStrafe = 75;
	public float ThinkTime = 0.25f;
	public float RateOfFire = 0.25f;
	public float PreferedDistance = 20.0f;
	public int	Score = 100;
	
	// state
	private float Health;
	private bool wantToStrafe = false;
	private float strafeDir = 0.0f;
	private float lastDecidedTime = 0.0f;
	private float lastFiredTime = 0.0f;	
	private Vector3 targetPosRecocnized = Vector3.zero;
	private Vector3 targetVelocityRecocnized = Vector3.zero;
	

	// Use this for initialization
	void Awake () {
		Health = MaxHealth;
		PreferedDistance = 20.0f;
	}
	
	// Update is called once per frame
	void Update () {
		GameObject target = GameObject.FindWithTag( "Player" );
		if( !target )
			return;

		// think
		if( Time.realtimeSinceStartup - lastDecidedTime > ThinkTime )
		{
			// recognize target pos and velocity
			targetPosRecocnized = target.transform.position;
			targetVelocityRecocnized = target.rigidbody.velocity;
			
			// deside if want to strafe
			if( !wantToStrafe ){
				if( Random.Range( 0, 100 ) < PercentToStrafe )
				{
					wantToStrafe = true;
					strafeDir = Random.Range( 0, 100 ) < 50 ? 1.0f : -1.0f;
				}
			}
			else
			{
				if( Random.Range( 0, 100 ) < 20 )
				{
					wantToStrafe = false;
					strafeDir = 0.0f;
				}
			}
			
			// change prefered distance to player relative to health;
			if( Health < 60.0f )
				PreferedDistance = 40.0f;
			if( Health < 30.0f )
				PreferedDistance = 10.0f;
			
			lastDecidedTime = Time.realtimeSinceStartup;
			
		}
		
		// if its in air, add extra gravity 
		bool onGround = Physics.Raycast( rigidbody.position, Vector3.down, 1.35f );
		if( !onGround )
			rigidbody.AddForce( Vector3.down * 3.0f * Time.deltaTime * 60.0f, ForceMode.Impulse );
		
		// try look at players future position
		float predictTime = Vector3.Magnitude( targetPosRecocnized - transform.position ) * 0.03f;
		Vector3 predictedPos = targetPosRecocnized + targetVelocityRecocnized * predictTime;
		Vector3 relativePos = predictedPos - transform.position;
		Vector3 relativeAngle = transform.InverseTransformDirection( relativePos );
		
		float rotAngle = 0.0f;
		if( relativeAngle.x >= 1.0f ) rotAngle = 1.0f;
		if( relativeAngle.x < -1.0f ) rotAngle = -1.0f;
		rigidbody.AddTorque( Vector3.up * rotAngle *  RotSpeed * Time.deltaTime * 60.0f );

		// fire at player 
		if( Mathf.Abs ( relativeAngle.x ) < 10.0f && relativePos.magnitude < 50.0f)
		{
			if( Time.timeSinceLevelLoad - lastFiredTime > RateOfFire )
			{
				Transform FirePos = transform.FindChild( "FireMuzzle" );
				Transform fireball = (Transform)Instantiate( Fireball, FirePos.position, FirePos.rotation );
				Physics.IgnoreCollision( fireball.collider, collider );
				lastFiredTime = Time.timeSinceLevelLoad;
			}
		}
		
		// move to player if it is far away
		if( relativePos.magnitude > PreferedDistance )
			rigidbody.AddForce( transform.forward * MoveSpeed * Time.deltaTime * 60.0f, ForceMode.Acceleration );

		// move away from player if it is too near
		if( relativePos.magnitude < PreferedDistance )
			rigidbody.AddForce( transform.forward * MoveSpeed * -1.0f * Time.deltaTime * 60.0f, ForceMode.Acceleration);
		
		// do strafing
		rigidbody.AddForce( transform.right * strafeDir * MoveSpeed * Time.deltaTime * 60.0f, ForceMode.Acceleration );
	}

	// Damage receiver
	void InflictDamage( float damage )
	{
		Health -= damage;
		
		// smoke
		float smokelevel =  ( MaxHealth - Health * 2 ) / MaxHealth;
		if( smokelevel > 0 )
		{
			Transform obj = transform.FindChild( "Smoke" );
			ParticleEmitter emitter = (ParticleEmitter) obj.GetComponent( "ParticleEmitter" );
			emitter.maxEmission = smokelevel * 20;
			emitter.minEmission = smokelevel * 20;
		}
		
		// apply damaged texture
		if( Health < MaxHealth * 0.333 )
			renderer.material.mainTexture = DamagedTexture2;
		else if( Health < MaxHealth * 0.666 )
			renderer.material.mainTexture = DamagedTexture;
		
		if( Health <= 0.0f )
			Death();
	}
	
	// Die!!
	void Death()
	{
		Transform explosion = (Transform)Instantiate( Explosion, transform.position, transform.rotation );
		explosion.renderer.material.color = this.renderer.material.color;
	
		int shards = 20;
		if( name.Contains( "Big" ) )
			shards = 40;
		
		for (int i=0; i<shards; i++ )
		{
			Transform shard = (Transform)Instantiate( Shard, transform.position, transform.rotation );
			shard.renderer.material.color = this.renderer.material.color;
			shard.localScale = Vector3.one * Random.Range( 0.25f, 0.5f );
			Vector3 pos;
			pos.x = transform.position.x + Random.Range( -2.0f, 2.0f );
			pos.y = transform.position.y + Random.Range( -1.0f, 1.0f );
			pos.z = transform.position.z + Random.Range( -2.0f, 2.0f );
			if( pos.y < 0.5f ) pos.y = 0.5f;
			shard.rigidbody.MovePosition( pos );
			shard.rigidbody.AddExplosionForce( 200.0f, transform.position, 50.0f );
		}
		
		// notify death to gamecontroller 
		GameObject obj = GameObject.Find( "GameController" );
		obj.SendMessage( "NotifyEnemyDeath", this.gameObject );
		obj.SendMessage( "UpdateScore", Score );

		DestroyObject( this.gameObject );
	}
}